home *** CD-ROM | disk | FTP | other *** search
- Path: news.microsoft.com!news
- From: a-cnadc@microsoft.com (Dann Corbit)
- Newsgroups: comp.lang.c
- Subject: Tough Question: The summary. WARNING LONG! - tough.cpp [1/1]
- Date: 20 Feb 1996 22:25:13 GMT
- Organization: Microsoft Corporation
- Message-ID: <4gdho9$mll@news.microsoft.com>
- NNTP-Posting-Host: 157.57.171.202
- Mime-Version: 1.0
- Content-Type: multipart/mixed;
- Boundary="*-*-*- Next Section -*-*-*"
- X-Newsreader: WinVN 0.93.14
-
- --*-*-*- Next Section -*-*-*
-
- I put most of the code samples that were submitted into a C
- driver and tested them. I may well have goofed some of them
- up, so code failures may be due to me.
-
- About half of them worked (up to 1000 as specified ) and half
- didn't.
-
- Of the versions that worked, there were huge differences in
- speed.
-
- Also included is a new version that is faster than any of
- the original suggestions. At the same time it is rather
- unsatisfying since it just looks up the answer in Dik Winter's
- table.
-
- Anyway, here are the routines, along with the test driver.
- The results for one of my machines are also included.
-
- --
- The opinions expressed in this message are my own personal views
- and do not reflect the official views of Microsoft Corporation.
- --*-*-*- Next Section -*-*-*
-
- /*************************************************************************
- PROBLEM DEFINITION:
-
- From: thecrow@iconn.net ( The Crow )
- Subject: Tough FACTORIAL math problem...
- Date: 13 Feb 1996 23:54:22 GMT
-
- Here is what I am trying to do, I want to find the last NON-ZERO digit of a
- given factorial. For instance,
-
- 5! = 120
-
- so the last non-zero digit is 2. I want to be able to do this up to 1000.
- Problem is, no matter how huge of a data-type you use, there isn't any way for
- the computer to handle 1000!, it is however possible to find the last non-zero
- digit somehow. One thing I have tried is as I went through mulitplying the
- series of numbers in the factorial ( 5 * 4 * 3 * 2 ) I would remove all the
- trailing ZEROS, I got this to work up to 789, but it wont work with 1000 and i
- am not really sure why. If anyone has a clue how I can do this let me know.
- ***************************************************************************/
- #ifdef TEST
- /* Dik Winter's table:
- If you wish to try, below follows a table with the last non-zero digits of
- all factorials up to ( and including ) 1000!. Check your output with this
- table.
- { ammended by d. corbit to include 0!}
- */
- static char gpszFactorialLastDigits[] = {
- "112642242888682886824484644846886822242822428662642"
- "24284484666264662648868244846886822242822428662644"
- "48468868222428224286626488682662644484644846224282"
- "24284484666264662648868266264224288868288682448462"
- "24284484666264662648868222428448466626466264886828"
- "86826626444846448462242822428448466626466264886826"
- "62642242888682886824484622428448466626466264886822"
- "24284484666264662648868222428448466626466264886828"
- "86826626444846448462242844846886822242822428662648"
- "86826626444846448462242888682662644484644846224284"
- "48468868222428224286626466264224288868288682448468"
- "86826626444846448462242866264224288868288682448466"
- "62642242888682886824484666264224288868288682448464"
- "48468868222428224286626422428448466626466264886824"
- "48468868222428224286626444846886822242822428662648"
- "86826626444846448462242822428448466626466264886826"
- "62642242888682886824484622428448466626466264886822"
- "24284484666264662648868288682662644484644846224282"
- "24284484666264662648868266264224288868288682448462"
- "24284484666264662648868222428448466626466264886822"
- };
- int iLastDigit( int i )
- {
- int j;
- j = ( int ) gpszFactorialLastDigits[i] - '0';
- return j;
- }
- #endif
-
- extern int __cdecl foo( int n );
- extern int __cdecl foo2( int n,int *x,int *y );
- extern int __cdecl lastdigit( unsigned long iVal );
- extern int __cdecl last_digit( int num );
- extern int __cdecl last_digit2( int num );
- extern int __cdecl last_digit2a( int num );
- extern int __cdecl last_non_zero_digit_of_factorial( int n );
- extern int __cdecl main( int argc,char **argv );
- extern int __cdecl odds( int n,int *x,int *f );
- extern int __cdecl rightmost_nonzero_digit( int n );
- extern int __cdecl rtDigit( int n );
- extern long __cdecl factorial_lsd( long n );
- extern unsigned long __cdecl eliminate_fives( unsigned long *i );
- extern unsigned long __cdecl eliminate_twos( unsigned long *i );
- extern unsigned long __cdecl right_digit( unsigned long i );
- extern void __cdecl getLastFactorialDigits( int *results,int n );
- extern int __cdecl ShowLastFactorialDigit( int results[], int n );
- static int __cdecl f( int i );
- static int __cdecl lastNonZeroDigitOfFactorial( int i );
-
- /* Dik Winter */
- int fact_lsd(long n)
- {
- long r = 1, i, i1;
- if(n > 1)
- {
- for(i = 1, i1 = 1; i <= n; i++, i1 = i)
- {
- /* cast out factors 5 */
- while(i1 % 5 == 0)
- {
- i1 /= 5;
- /* multiply by 5 divide by 10, equivalent to divide by 2. */
- r >>= 1;
- /* except for 1! and 0! the last non-zero digit is even. */
- if(r & 1) r += 5;
- }
- r = (r * i1) % 10;
- }
- }
- return r;
- }
- /* Dann Corbit */
- long factorial_lsd( long n )
- {
- long result=1, i;
-
- if ( n > 1 )
- for ( i=1; i<=n; i++ )
- {
- result *= i;
- while( result%10 == 0 )
- {
- result /= 10;
- }
- result %= 100000L;
- }
- return result%10;
- }
-
- //From:ian@rsd.bel..alcatel.be ( Ian Ward )
- int last_digit ( int num )
- {
- static int tens [4] = {
- 8, 4, 2, 6
- };
- static int singles [10] = {
- 1, 1, 2, 6, 4, 2, 2, 4, 2, 8
- };
- int single = singles [num % 10];
- if ( num > 9 ) single *= tens [( num/10-1 ) % 4];
- return single % 10;
- }
-
- //From: ian@rsd.bel.alcatel.be ( Ian Ward )
- int last_digit2 ( int num )
- {
- static int fred [] =
- {
- 1, 1, 2, 6, 4, 2, 2, 4, 2, 8, 8, 8,
- 6, 8, 2, 6, 6, 2, 6, 4, 4, 4, 8, 4,
- 6, 8, 8, 6, 8, 2, 2, 2, 4, 2, 8, 4,
- 4, 8, 4, 6, 6, 6, 2, 6, 4, 2, 2, 4, 2, 8
- };
- return fred[ num>9 ? 10+( ( num-10 )%40 ) : num ];
- }
-
- //From: kcline@sun132.spd.dsccc.com ( Kevin Cline )
- static int table[9][9] =
- {
- {1, 2, 0, 4, 0, 6, 0, 8, 0 },
- {2, 4, 0, 8, 0, 2, 0, 6, 0 },
- {0, 6, 0, 2, 0, 8, 0, 6, 0 },
- {0, 8, 0, 6, 0, 4, 0, 2, 0 },
- {0, 4, 0, 8, 0, 2, 0, 6, 0 },
- {0, 2, 0, 4, 0, 6, 0, 8, 0 },
- {0, 4, 0, 8, 0, 2, 0, 6, 0 },
- {0, 6, 0, 2, 0, 8, 0, 6, 0 },
- {0, 8, 0, 6, 0, 4, 0, 2, 0 }
- };
-
-
-
-
- int last_non_zero_digit_of_factorial( int n ) {
- int digit = 1, i;
- for ( i = 2; i < n ; ++i ) {
- while ( i % 10 == 0 ) i /= 10;
- digit = table[digit-1][i-1];
- }
- return digit;
- }
-
- //From: jscuste@sandia.gov ( Jon Custer )
- /* Determine last non-zero digit in a factorial */
- /*
- General algorithm:
- 1 ) Eliminate all powers of 2 as they come along. Keep track of _net_
- number of powers of 2 ( see next step ). Last digit of this
- factor is easy to determine in a look up table.
- 2 ) Eliminate all powers of 5 as they come along. Take away powers of
- 2, leaving _net_ powers of 2, to convert to powers of 10.
- There are always more powers of 2 than powers of 5.
- All powers of 10 result in no change to last significant digit.
- 3 ) Multiply remainder of factor with previous residual. Truncate
- to last digit, and reserve for next iteration. Multiply by
- last digit of _net_ powers of two, take last digit as answer.
- 4 ) Print out result, and loop to next.
- */
-
- #include <stdlib.h>
- #include <stdio.h>
-
- /* function prototypes */
- unsigned long right_digit( unsigned long i );
- unsigned long eliminate_fives( unsigned long *i );
- unsigned long eliminate_twos( unsigned long *i );
-
- int lastdigit( unsigned long iVal ){
-
- /* keep track of powers of 5s, powers of 2s so far */
- unsigned long p_of_5 = 0ul;
- unsigned long p_of_2 = 0ul;
-
- static unsigned long lookup[4] = {
- 6ul, 2ul, 4ul, 8ul
- };
-
- unsigned long one;
- unsigned long k;
-
-
-
- /* special cases here - 0! and 1! */
- if ( iVal == 0 || iVal == 1 )
- return 1;
-
- /* Initialize last digit */
- one = 1ul;
-
- /* drop all 2's from next factor, keep track of number */
- p_of_2 += eliminate_twos( &iVal );
-
- /* drop all 5's from factor */
- k = eliminate_fives( &iVal );
- p_of_5 += k; /* keep track of total 5's */
- p_of_2 -= k; /* eliminate 2's to make 10s */
-
- /* multiply remainder and take last digit ( always non-zero ) */
- one = right_digit( one*iVal );
-
- /* now correct for all the powers of two so far */
- return right_digit( one*lookup[( p_of_2 % 4 )] );
-
- }
-
- unsigned long right_digit( unsigned long i ){
- return ( i % 10ul );
- }
-
- /* returns number of powers of 5 found in this integer, alters integer */
- unsigned long eliminate_fives( unsigned long *i ){
- unsigned long q;
-
- q = 0ul;
- while( !( *i % 5ul ) ){
- *i = *i/5ul;
- q++;
- }
- return( q );
- }
-
- /* returns number of powers of 2 found in this integer, alters integer */
- unsigned long eliminate_twos( unsigned long *i ){
- unsigned long q;
-
- q = 0ul;
- while( !( *i % 2ul ) ){
- *i = *i/2ul;
- q++;
- }
- return( q );
- }
-
- //From: kcline@sun152.spd.dsccc.com ( Kevin Cline )
- #include <iostream.h>
- #include <stdlib.h>
-
- // last digits of powers of two
- static int t0[] = {
- 6, 2, 4, 8 };
-
- // log base two ( mod 5 ) of 0! - 4!
- static int t1[] = {
- 0, 0, 1, 0, 2 };
-
- static int f( int i ) {
- if ( i == 0 ) return 0;
- return t1[i % 5] + i/5 + f( i/5 );
- }
-
- static int lastNonZeroDigitOfFactorial( int i ) {
- if ( i < 2 ) return 1;
- return t0[f( i ) % 4];
- }
- //From: ian@rsd.bel.alcatel.be ( Ian Ward )
-
- int last_digit2a ( int num )
- {
- static int fred [] =
- {
- 2, 6, 4, 2, 2, 4, 2, 8, 8, 8, 6, 8, 2, 6, 6, 2, 6, 4, 4, 4,
- 8, 4, 6, 8, 8, 6, 8, 2, 2, 2, 4, 2, 8, 4, 4, 8, 4, 6, 6, 6
- };
- return num>1?fred[( num-2 )%40]:1;
- }
-
- //From: kaskel@kirkuk.berkeley.edu ( Bruce Kaskel )
- int foo( int n )
- /* the input is n for n! */
- {
- int i, j, u=1, t=0;
- for( i=1;i<=n;i++ )
- {
- j=i;
- while( !( j&1 ) ) {
- j>>=1;
- t++;
- }
- while( !( j%5 ) ) {
- j/=5;
- t--;
- }
- u=( u*( j%10 ) )%10;
- }
- if( t ) {
- t&=3;
- if( !t ) t=4;
- } /* if t>0, only need t mod 4 */
- for( i=0;i<t;i++ ) u=( u<<1 )%10; /* include factors of 2 */
- return( u ); /* The rightmost non-zero digit of n! is u */
- }
-
- //From: Chris Peikert <cpeikert@kamsc.seds.org>
- #include <iostream.h>
-
- int rtDigit( int n ) {
- int i;
- long dig=1;
- for( i=2; i<=n; i++ ) { // computing factorial
-
- dig *= i;
- while( !( dig % 10 ) ) // chop of righthand 0s
- dig /= 10;
- dig %= 1000; // keep last three nonzero digits
-
- }
- return ( dig % 10 ); // return last digit
-
- }
-
- //From: gusty@clark.net ( Harlan Messinger )
- // assume sufficient space was preallocated ( n+1 bytes )
- void getLastFactorialDigits( int results[], int n ) {
-
- results[0] = 1;
- results[1] = 1;
- for ( int i = 2; i <= n; ++i ) {
- results[i] = results[i - 1] * i;
- while ( results[i] % 10 == 0 )
- results[i] /= 10;
- results[i] = results[i] % 10;
- }
- return;
- }
- int ShowLastFactorialDigit( int results[], int n ) {
- return results[n];
- }
-
- //From: kaskel@kirkuk.berkeley.edu ( Bruce Kaskel )
- int a[20]={
- 1, 1, 1, 3, 3, 3, 3, 1, 1, 9, 9, 9, 9, 7, 7, 7, 7, 9, 9, 1
- };
- int b[4]={
- 6, 2, 4, 8
- };
-
- int rightmost_nonzero_digit( int n )
- {
- int u, t;
- if( n<2 ) return( 1 );
- foo2( n, &u, &t );
- return( ( u*b[t] )%10 );
- }
-
- int foo2( int n,int *x, int *y )
- {
- int i=1, j=0, m=n>>1, u, f;
- odds( n, &u, &f );
- if ( m>1 ) foo2( m, &i, &j );
- *x=( u*i )%10;
- *y=( m+( 4-f )+j )&3;
- return 0;
- }
-
- int odds( int n, int *x, int *f )
- {
- int q=1, r=0, s=n/5;
- if( s>2 ) odds( s, &q, &r );
- *x=( q*a[n%20] )%10;
- *f=( r+( ( s+1 )>>1 ) )&3;
- return 0;
- }
- #ifdef TEST
-
- #ifndef FALSE
- #define FALSE 0
- #endif
-
- #ifndef TRUE
- #define TRUE !FALSE
- #endif
-
- int results[1001];
- #include <time.h>
- int main( int argc, char **argv )
- {
-
- int i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, ia, ib, ic;
- char b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc;
- char pszString[80];
- int n;
- int j;
-
- b0 = b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b9 = ba = bb = bc = TRUE;
- getLastFactorialDigits( results, 1000 );
- /* Agreement test: */
- puts( "*** AGREEMENT TESTS ***" );
- for ( n = 1; n <= 1000; n++ )
- {
- i0 = iLastDigit( n ); /* This was is ASSUMED to be infallable */
- if ( b1 )
- i1 = last_digit( n );
- if ( b2 )
- i2 = last_digit2( n );
- if ( b3 )
- i3 = last_digit2a( n );
- if ( b4 )
- i4 = last_non_zero_digit_of_factorial( n );
- if ( b5 )
- i5 = rightmost_nonzero_digit( n );
- if ( b6 )
- i6 = rtDigit( n );
- if ( b7 )
- i7 = factorial_lsd( n );
- if ( b8 )
- i8 = f( n );
- if ( b9 )
- i9 = lastNonZeroDigitOfFactorial( n );
- if ( ba )
- ia = foo( n );
- if ( bb )
- ib = ShowLastFactorialDigit( results, n );
- if ( bc )
- ic = fact_lsd( ( long ) n );
-
- if ( i0 != i1 && b1 )
- {
- b1 = FALSE;
- printf( "Method 1 failed at %d.\n", n );
- }
-
- if ( i0 != i2 && b2 )
- {
- b2 = FALSE;
- printf( "Method 2 failed at %d.\n", n );
- }
-
- if ( i0 != i3 && b3 )
- {
- b3 = FALSE;
- printf( "Method 3 failed at %d.\n", n );
- }
-
- if ( i0 != i4 && b4 )
- {
- b4 = FALSE;
- printf( "Method 4 failed at %d.\n", n );
- }
-
- if ( i0 != i5 && b5 )
- {
- b5 = FALSE;
- printf( "Method 5 failed at %d.\n", n );
- }
-
- if ( i0 != i6 && b6 )
- {
- b6 = FALSE;
- printf( "Method 6 failed at %d.\n", n );
- }
-
- if ( i0 != i7 && b7 )
- {
- b7 = FALSE;
- printf( "Method 7 failed at %d.\n", n );
- }
-
- if ( i0 != i8 && b8 )
- {
- b8 = FALSE;
- printf( "Method 8 failed at %d.\n", n );
- }
-
- if ( i0 != i9 && b9 )
- {
- b9 = FALSE;
- printf( "Method 9 failed at %d.\n", n );
- }
-
- if ( i0 != ia && ba )
- {
- ba = FALSE;
- printf( "Method a failed at %d.\n", n );
- }
-
- if ( i0 != ib && bb )
- {
- bb = FALSE;
- printf( "Method b failed at %d.\n", n );
- }
- if ( i0 != ic && bc )
- {
- bc = FALSE;
- printf( "Method c failed at %d.\n", n );
- }
-
- }
- if (!b1 )
- puts( "last_digit( n ) failed." );
- else
- puts( "last_digit( n ) succeeded!" );
- if (!b2 )
- puts( "last_digit2( n ) failed." );
- else
- puts( "last_digit2( n ) succeeded!" );
- if (!b3 )
- puts( "last_digit2a( n ) failed." );
- else
- puts( "last_digit2a( n ) succeeded!" );
- if (!b4 )
- puts( "last_non_zero_digit_of_factorial( n ) failed." );
- else
- puts( "last_non_zero_digit_of_factorial( n ) succeeded." );
- if (!b5 )
- puts( "rightmost_nonzero_digit( n ) failed." );
- else
- puts( "rightmost_nonzero_digit( n ) succeeded!" );
- if (!b6 )
- puts( "rtDigit( n ) failed." );
- else
- puts( "rtDigit( n ) succeeded!" );
- if (!b7 )
- puts( "factorial_lsd( n ) failed." );
- else
- puts( "factorial_lsd( n ) succeeded!" );
- if (!b8 )
- puts( "f( n ) failed." );
- else
- puts( "f( n ) succeeded." );
- if (!b9 )
- puts( "lastNonZeroDigitOfFactorial( n ) failed." );
- else
- puts( "lastNonZeroDigitOfFactorial( n ) succeeded!" );
- if (!ba )
- puts( "foo( n ) failed." );
- else
- puts( "foo( n ) succeeded!" );
- if (!bb )
- puts( "ShowLastFactorialDigit( results, n ) failed." );
- else
- puts( "ShowLastFactorialDigit( results, n ) succeeded!" );
- if (!bc )
- puts( "fact_lsd( n ) failed." );
- else
- puts( "fact_lsd( n ) succeeded!" );
-
-
- puts( "*** SPEED TESTS ***" );
- printf("Start for direct lookup = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- i0 = iLastDigit( n ); /* This was is ASSUMED to be infallable */
- }
- printf("Start for rightmost_nonzero_digit( n ) = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- i5 = rightmost_nonzero_digit( n );
- }
- printf("Start for factorial_lsd( n ) = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- i7 = factorial_lsd( n );
- }
- printf("Start for lastNonZeroDigitOfFactorial( n ) = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- i9 = lastNonZeroDigitOfFactorial( n );
- }
- printf("Start for foo( n ) = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- ia = foo( n );
- }
- printf("Start for fact_lsd( n ) = %s\n", _strtime( pszString ) );
- for ( j = 0; j < 100; j++ )
- for ( n = 1; n <= 1000; n++ )
- {
- ia = fact_lsd( n );
- }
- printf("End for fact_lsd( n ) = %s\n", _strtime( pszString ) );
-
- return 0;
- }
- /*
- *** AGREEMENT TESTS ***
- Method 8 failed at 1.
- Method 4 failed at 2.
- Method 1 failed at 15.
- Method 2 failed at 15.
- Method 3 failed at 15.
- Method b failed at 15.
- Method 6 failed at 375.
- last_digit( n ) failed.
- last_digit2( n ) failed.
- last_digit2a( n ) failed.
- last_non_zero_digit_of_factorial( n ) failed.
- rightmost_nonzero_digit( n ) succeeded!
- rtDigit( n ) failed.
- factorial_lsd( n ) succeeded!
- f( n ) failed.
- lastNonZeroDigitOfFactorial( n ) succeeded!
- foo( n ) succeeded!
- ShowLastFactorialDigit( results, n ) failed.
- fact_lsd( n ) succeeded!
- *** SPEED TESTS ***
- Start for direct lookup = 14:11:20
- Start for rightmost_nonzero_digit( n ) = 14:11:20
- Start for factorial_lsd( n ) = 14:11:28
- Start for lastNonZeroDigitOfFactorial( n ) = 14:14:10
- Start for foo( n ) = 14:14:11
- Start for fact_lsd( n ) = 14:17:42
- End for fact_lsd( n ) = 14:20:17
- */
- #endif
- --*-*-*- Next Section -*-*-*--
-
-